package com.bzframework.core.auth.config;

import com.bzframework.core.auth.Authorization;
import com.bzframework.core.response.exception.NoAuthorizationException;
import com.bzframework.core.utils.StaticRequestUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;

/**
 * AopAuthorizationController
 */
@Slf4j
public class AopAuthorizationController extends StaticMethodMatcherPointcutAdvisor implements MethodInterceptor {

    private AuthenticateProperties authenticateProperties;

    public AopAuthorizationController(AuthenticateProperties authenticateProperties) {
        setAdvice(this);
        this.authenticateProperties = authenticateProperties;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        Authorization authorization = AnnotationUtils.findAnnotation(targetClass, Authorization.class);
        if (authorization != null && authorization.ignore()) {
            return false;
        }
        return AnnotationUtils.findAnnotation(targetClass, RestController.class) != null
                || AnnotationUtils.findAnnotation(targetClass, Controller.class) != null
                || AnnotationUtils.findAnnotation(targetClass, RequestMapping.class) != null;
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        if (excludeDocument(methodInvocation)) {
            return doProceed(methodInvocation);
        }

        Authorization authorization = methodInvocation.getMethod().getAnnotation(Authorization.class);
        if (authorization != null && authorization.ignore()) {
            return doProceed(methodInvocation);
        }

        String accessToken = StaticRequestUtils.getAccessTokenByHeader();
        if (accessToken == null || accessToken.isEmpty()) {
            throw new NoAuthorizationException();
        }
        // todo 后续添加校验token是否有效逻辑

        return doProceed(methodInvocation);
    }

    @SneakyThrows
    private <T> T doProceed(MethodInvocation invocation) {
        return (T) invocation.proceed();
    }

    private boolean excludeDocument(MethodInvocation methodInvocation) {
        String packageName = methodInvocation.getMethod().getDeclaringClass().getPackageName();
        return authenticateProperties
                .getExcludes()
                .stream()
                .anyMatch(packageName::startsWith);
    }
}
